﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "CmdPickWithAction.h"
#include "IPick.h"
#include "Vector3d.h"
#include "IDocument.h"
#include "ActionBase.h"
#include "ISelection.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "IPickResult.h"
#include "IUiDocument.h"
#include "IPickEventArgs.h"
#include "IPickCandidates.h"
#include "GcmpCommandState.h"
#include "IPickEventHandler.h"
#include "UiDocumentViewUtils.h"
#include "IGraphicsNodeReference.h"
#include "PickNodeReferenceOption.h"
#include "IPickNodeReferenceAction.h"

#include "PickFilterDlg.h"
#include "CommandRegister.h"
#include "SampleUIUtils.h"
#include "PickSnapCommandIds.h"
#include "CmdPickModuleEntry.h"
#include "PickAction\SamplePickNodeReferenceAction.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 


using namespace gcmp;
using namespace Sample;

namespace Sample
{
#pragma region 拾取过滤器和拾取事务
    // 拾取过滤器
    class PickWithActionFilter : public IPickFilter
    {
    public:
        PickWithActionFilter(PickFilterDlg* pPickFilterDlg)
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(pPickFilterDlg, L"pPickFilterDlg为空",L"GDMP",L"2023-12-30");
            m_pickFilterDlg = pPickFilterDlg;
        }

        virtual bool AllowElement(const gcmp::ElementId& elementId) const override
        {
            // 根据Ribbon菜单的AllowElement选项
            return m_pickFilterDlg->AllowElement();
        }

        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& graphicsNodeReference) const override
        {
            // 根据Ribbon菜单的AllowGraphicsNode选项
            return m_pickFilterDlg->AllowGraphicsNode();
        }

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pPickTarget) override
        {
            // 根据拾取过滤栏设置IPickTarget选项
            m_pickFilterDlg->SetPickTarget(pPickTarget);
            return true;
        }
    private:
        PickFilterDlg* m_pickFilterDlg;
    };

    class PickWithActionEventHandler : public gcmp::IPickEventHandler
    {
    public:
        virtual void On(gcmp::IPickEventArgs* pArgs) override
        {
            // 在底部状态栏按照拾取候选集顺序显示拾取候选项
            const IPickResult& pPickResult = pArgs->GetPickCandidates();
            std::wstring tooltip = L"IPickEventArgs.IPickResult: ";
            for (const OwnerPtr<IGraphicsNodeReference>& itor : pPickResult.GetAllGraphicsNodeReferences())
            {
                DBG_WARN_AND_RETURN_VOID_UNLESS(itor, L"itor为空",L"GDMP",L"2023-12-30");
                tooltip += itor->GetDescriptionString() + L"; ";
                // TODO:GBMP-45577 信息太长会导致主窗口变形
                break;
            }
            OwnerPtr<IPickResult> opRslt = IPickResult::Create();
            pArgs->SetPickCandidates(*opRslt);
            SampleUIUtils::SetStatusBarPromptMessage(tooltip);
        }
    };
#pragma endregion 拾取过滤器和拾取事务

#pragma region 使用IPickNodeReferenceAction的Action
    class PickWithActionAction : public gcmp::ActionBase
    {

    public:
        PickWithActionAction() :
            m_isUserCancelled(false), 
            m_pickNodeReferenceExchangeData(true, &m_pickedReferences, &m_isUserCancelled, &m_ptMousePoint),
            m_pickNodeReferenceOption()
        {
        }

        virtual void InitAction(IUiView* pCurrentView) override
        {
            EnterPickElementState();
        }

        void OnMove(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& pos)
        {
            // 这里可以绘制用于预览的临时图元
        }

        virtual void OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam) override
        {
            IUiDocument* pUIDocument = pCurrentView->GetUiDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pUIDocument, L"pUIDocument为空",L"GDMP",L"2023-12-30");
            IDocument* pDocument = pUIDocument->GetDbDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDocument, L"pDocument为空",L"GDMP",L"2023-12-30");

            // 不是所有的拾取操作都需要选择。根据Ribbon菜单的选择拾取图元，决定是否将拾取候选集放入选择集
            if (CmdPickModuleEntry::DoSelectPicked())
            {
                ISelection* pSelection = ISelection::Get();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pSelection, L"pSelection为空",L"GDMP",L"2023-12-30");
                pSelection->Clear(pDocument);

                for (const OwnerPtr<IGraphicsNodeReference>& itor : m_pickedReferences)
                {
                    pSelection->AddGraphicsNodeReference(pDocument, *itor.get());
                    std::wstring nodeIdPath = L"";
                    for (auto nodeId : itor->GetGraphicsNodeIdPath())
                    {
                        nodeIdPath += StringUtil::ToWString(nodeId.AsInt()) + L";";
                    }
                    OutputDebugStringW(nodeIdPath.c_str());
                }

                // 记得刷新视图，否则高亮预高亮显示可能不会立刻刷新
                pUIDocument->UpdateView();
            }

            //如果是多选，用户在IPickNodeReferenceAction中取消操作，或者拾取候选集为空，那么结束Action；否则循环拾取
            if(m_pickNodeReferenceOption.bMultiSelect || m_isUserCancelled || m_pickedReferences.empty())
            {
                MarkFinishStatus(ActionFinishStatus::Successful);
                SampleUIUtils::SetStatusBarPromptMessage(L"");
            }
            else
            {
                EnterPickElementState();
            }
        }

    private:
        void EnterPickElementState()
        {
            m_pickedReferences.clear();

            IUiDocument* pUiDocument = UiDocumentViewUtils::GetCurrentUiDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pUiDocument, L"pUiDocument为空",L"GDMP",L"2023-12-30");
            gcmp::IDocument* pDoc = pUiDocument->GetDbDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2023-12-30");

            // 通过拾取过滤栏设置PickNodeReferenceExchangeData,PickNodeReferenceOption和IPickFilter
            PickFilterDlg::Get()->SetPickNodeReferenceExchangeData(m_pickNodeReferenceExchangeData);
            PickFilterDlg::Get()->SetPickNodeReferenceOption(m_pickNodeReferenceOption);
            OwnerPtr<PickWithActionFilter> opPickFilter = NEW_AS_OWNER_PTR(PickWithActionFilter, PickFilterDlg::Get());
            DBG_WARN_AND_RETURN_VOID_UNLESS(opPickFilter, L"opPickFilter为空",L"GDMP",L"2023-12-30");

            // IPickNodeReferenceAction移动中的回调，在例如拾取面创建构件时可以绘制预览图形
            m_pickNodeReferenceOption.SetMoveCallback(IPickNodeReferenceAction::CreateMoveCallback(&PickWithActionAction::OnMove, this));

            // 设置移动拾取中的回调事务
            OwnerPtr<PickWithActionEventHandler> opPickEventHandler = NEW_AS_OWNER_PTR(PickWithActionEventHandler);
            m_pickNodeReferenceOption.SetPostEventHandler(opPickEventHandler.get());

            // 设置没有选中时结束命令还是继续拾取 
            bool bPickNullReturnPos = CmdPickModuleEntry::DoPickNullReturnPos();
            m_pickNodeReferenceOption.SetPickNullReturnPos(bPickNullReturnPos, &m_pickedPos);

            // 设置拾取的像素容差
            int tolerance = CmdPickModuleEntry::GetTolerance();
            m_pickNodeReferenceOption.SetPickPixelTolerance(tolerance);

            // 创建并启动IPickNodeReferenceAction
            if (CmdPickModuleEntry::DoUseCustomeAction())
            {
                gcmp::OwnerPtr<IAction> opPickNodeReferenceAction = SamplePickNodeReferenceAction::Create(
                    m_pickNodeReferenceExchangeData, TransferOwnership(opPickFilter), m_pickNodeReferenceOption);
                DBG_WARN_AND_RETURN_VOID_UNLESS(opPickNodeReferenceAction, L"opPickNodeReferenceAction为空", L"GDMP", L"2023-12-30");
                StartChildAction(TransferOwnership(opPickNodeReferenceAction));
            }
            else
            {
                gcmp::OwnerPtr<IAction> opPickNodeReferenceAction = IPickNodeReferenceAction::Create(
                    m_pickNodeReferenceExchangeData, TransferOwnership(opPickFilter), m_pickNodeReferenceOption);
                DBG_WARN_AND_RETURN_VOID_UNLESS(opPickNodeReferenceAction, L"opPickNodeReferenceAction为空", L"GDMP", L"2023-12-30");
                StartChildAction(TransferOwnership(opPickNodeReferenceAction));
            }
        }

    private:
        // 缓存IPickNodeReferenceAction的拾取输出结果
        bool m_isUserCancelled;
        Vector3d m_pickedPos;
        gcmp::Vector3d m_ptMousePoint;
        gcmp::GraphicsNodeReferenceOwnerPtrVector m_pickedReferences;
        gcmp::PickNodeReferenceExchangeData m_pickNodeReferenceExchangeData;
        gcmp::PickNodeReferenceOption m_pickNodeReferenceOption;
    };
#pragma endregion 使用拾取API的Action
}

#pragma region CmdPickWithAction
REGISTER_COMMAND(CmdPickWithAction)

CmdPickWithAction::CmdPickWithAction() : CommandBase(ID_CMD_PICK_WITH_ACTION)
{
}

CmdPickWithAction::~CmdPickWithAction(){}

gcmp::OwnerPtr<gcmp::IAction> CmdPickWithAction::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    if (PickFilterDlg* dlg = PickFilterDlg::Get())
    {
        dlg->show();
    }
    return NEW_AS_OWNER_PTR(PickWithActionAction);
}

// 是否在启动Command前清空之前的选择集
bool CmdPickWithAction::ShouldClearSelectionBeforeExecution() const
{
    bool clearSelectionBeforeExecution = CmdPickModuleEntry::DoClearSelectionBeforeCmd();
    return clearSelectionBeforeExecution;
}

bool CmdPickWithAction::IsEnabled() const
{
    return GcmpCommandState::IsInDocumentEnvironment() && (CmdPickModuleEntry::GetPickMethod() == PickMethod::PickNodeReferenceAction);
}

bool CmdPickWithAction::IsVisible() const
{
    return GcmpCommandState::IsInDocumentEnvironment();
}
#pragma endregion CmdPickWithAction

